/*=============================================================================
D3DPostProcess : Direct3D specific post processing special effects
Copyright (c) 2001 Crytek Studios. All Rights Reserved.

Revision history:
* 23/02/2005: Re-factored/Converted to CryEngine 2.0 by Tiago Sousa
* Created by Tiago Sousa

=============================================================================*/

#include "StdAfx.h"
#include "DriverD3D.h"
#include "I3DEngine.h"
#include "D3DPostProcess.h"

#pragma warning(disable: 4244)

SD3DPostEffectsUtils SD3DPostEffectsUtils::m_pInstance;

/////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////

SD3DSurface *SD3DPostEffectsUtils::GetDepthSurface( CTexture *pTex )
{
  if( pTex->GetFlags() & FT_USAGE_MSAA && gRenDev->m_RP.m_MSAAData.Type)
    return &gcpRendD3D->m_DepthBufferOrigMSAA;

  return &gcpRendD3D->m_DepthBufferOrig;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

void SD3DPostEffectsUtils::ResolveRT( CTexture *&pDst, const RECT * pSrcRect )
{    
  assert(pDst);
  if(!pDst)
	  return;

  int iTempX, iTempY, iWidth, iHeight;
  gcpRendD3D->GetViewport(&iTempX, &iTempY, &iWidth, &iHeight);

#if defined (DIRECT3D9)
  D3DSurface *pBackSurface= gcpRendD3D->m_pNewTarget[0]->m_pTarget;
  D3DSurface *pTexSurf= pDst->GetSurface(0, 0);



























		RECT srcRect = { 0, 0, iWidth, iHeight };
		RECT dstRect = { 0, 0, pDst->GetWidth(), pDst->GetHeight() };

		gcpRendD3D->m_RP.m_PS[gcpRendD3D->m_RP.m_nProcessThreadID].m_RTCopied++; 
		gcpRendD3D->m_RP.m_PS[gcpRendD3D->m_RP.m_nProcessThreadID].m_RTCopiedSize += pDst->GetDeviceDataSize();
		gcpRendD3D->m_pd3dDevice->StretchRect(pBackSurface, pSrcRect ? pSrcRect : &srcRect, pTexSurf, &dstRect, D3DTEXF_NONE);


  SAFE_RELEASE(pTexSurf);

#elif defined (DIRECT3D10)

  CDeviceTexture *pDstResource = pDst->GetDevTexture();        
  ID3D11RenderTargetView* pOrigRT = gcpRendD3D->m_pNewTarget[0]->m_pTarget;
  if (pOrigRT && pDstResource)
  {
    D3D11_BOX box;
    ZeroStruct(box);
		if (pSrcRect)
		{
			box.left = pSrcRect->left;
			box.right = pSrcRect->right;
			box.top = pSrcRect->top;
			box.bottom = pSrcRect->bottom;
		}
		else
		{
			box.right = min(pDst->GetWidth(),gcpRendD3D->m_pNewTarget[0]->m_Width);
			box.bottom = min(pDst->GetHeight(),gcpRendD3D->m_pNewTarget[0]->m_Height);
		}
    box.back = 1;






		ID3D11Resource *pSrcResource;
		pOrigRT->GetResource( &pSrcResource );


    HRESULT hr = 0;
    gcpRendD3D->m_RP.m_PS[gcpRendD3D->m_RP.m_nProcessThreadID].m_RTCopied++; 
    gcpRendD3D->m_RP.m_PS[gcpRendD3D->m_RP.m_nProcessThreadID].m_RTCopiedSize += pDst->GetDeviceDataSize();

    gcpRendD3D->m_pd3dDeviceContext->CopySubresourceRegion(pDstResource->Get2DTexture(), 0, 0, 0, 0, pSrcResource, 0, &box);      
    SAFE_RELEASE(pSrcResource);
  }

#endif    
}

void SD3DPostEffectsUtils::CopyTextureToScreen(CTexture *&pSrc, const RECT * pSrcRegion)
{
	static CCryNameTSCRC pRestoreTechName("TextureToTexture");
	PostProcessUtils().ShBeginPass(CShaderMan::m_shPostEffects, pRestoreTechName, FEF_DONTSETTEXTURES | FEF_DONTSETSTATES);
	gRenDev->FX_SetState(GS_NODEPTHTEST);
	PostProcessUtils().SetTexture(pSrc, 0, FILTER_POINT);
	PostProcessUtils().DrawFullScreenQuad(pSrc->GetWidth(), pSrc->GetHeight(), 0, true, pSrcRegion);
	PostProcessUtils().ShEndPass();	
}

void SD3DPostEffectsUtils::CopyScreenToTexture(CTexture *&pDst, const RECT * pSrcRegion)
{
  int iTempX, iTempY, iWidth, iHeight;
  gcpRendD3D->GetViewport(&iTempX, &iTempY, &iWidth, &iHeight);

  ResolveRT( pDst, pSrcRegion );
}






























////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

void SD3DPostEffectsUtils::StretchRect(CTexture *pSrc, CTexture *&pDst, bool bCoarseDepthInAlpha, bool bDecodeSrcRGBK, bool bEncodeDstRGBK, bool bBigDownsample, bool bMaxDownsample, bool bBindMultisampled) 
{
  if(!pSrc || !pDst) 
  {
    return;
  }

  PROFILE_LABEL_PUSH( "STRETCHRECT_EMU" );

  PROFILE_SHADER_START

  uint64 nSaveFlagsShader_RT = gRenDev->m_RP.m_FlagsShader_RT;
  gRenDev->m_RP.m_FlagsShader_RT &= ~(g_HWSR_MaskBit[HWSR_SAMPLE0]|g_HWSR_MaskBit[HWSR_SAMPLE1]|g_HWSR_MaskBit[HWSR_SAMPLE2]|g_HWSR_MaskBit[HWSR_SAMPLE4]|g_HWSR_MaskBit[HWSR_SAMPLE5]);

  // Get current viewport
  int iTempX, iTempY, iWidth, iHeight;
  gRenDev->GetViewport(&iTempX, &iTempY, &iWidth, &iHeight);
  bool bResample=0;

  if(pSrc->GetWidth()!=pDst->GetWidth() && pSrc->GetHeight()!=pDst->GetHeight())
  {
    bResample = 1;
  }

  gcpRendD3D->FX_PushRenderTarget(0, pDst, NULL); 
  gcpRendD3D->RT_SetViewport(0, 0, pDst->GetWidth(), pDst->GetHeight());     

  bool bEnableRTSample0 = bBindMultisampled;




  if( bEnableRTSample0 )
    gRenDev->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_SAMPLE0];  

	if( bCoarseDepthInAlpha ) // enable max operation instead of average, for downsampling alpha
		gRenDev->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_SAMPLE1];

	if( bDecodeSrcRGBK ) // decode RGBK src texture
		gRenDev->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_SAMPLE2];
	if( bMaxDownsample ) // take maximum depth from the 4 samples
		gRenDev->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_SAMPLE4];
	if( bEncodeDstRGBK ) // encode RGBK dst texture
		gRenDev->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_SAMPLE5];

  if(!bResample)
  {
    static CCryNameTSCRC pTechName("TextureToTexture");                 
    ShBeginPass(CShaderMan::m_shPostEffects, pTechName, FEF_DONTSETTEXTURES | FEF_DONTSETSTATES);
  }
  else
  { 
    static CCryNameTSCRC pTechName("TextureToTextureResampled");     
    ShBeginPass(CShaderMan::m_shPostEffects, pTechName, FEF_DONTSETTEXTURES | FEF_DONTSETSTATES);
  }

  gRenDev->FX_SetState(GS_NODEPTHTEST);   

  // Get sample size ratio (based on empirical "best look" approach)
  float fSampleSize = ((float)pSrc->GetWidth()/(float)pDst->GetWidth()) * 0.5f;

  // Set samples position
  //float s1 = fSampleSize / (float) pSrc->GetWidth();  // 2.0 better results on lower res images resizing        
  //float t1 = fSampleSize / (float) pSrc->GetHeight();       

	CTexture *pOffsetTex = bBigDownsample ? pDst : pSrc;

  float s1 = 0.5f / (float) pOffsetTex->GetWidth();  // 2.0 better results on lower res images resizing        
  float t1 = 0.5f / (float) pOffsetTex->GetHeight();       

  Vec4 pParams0, pParams1;

	if (bBigDownsample)
	{
		// Use rotated grid + middle sample (~quincunx)
		pParams0=Vec4(s1*0.96f, t1*0.25f, -s1*0.25f, t1*0.96f); 
		pParams1=Vec4(-s1*0.96f, -t1*0.25f, s1*0.25f, -t1*0.96f);  
	}
	else
	{
		// Use box filtering (faster - can skip bilinear filtering, only 4 taps)
		pParams0=Vec4(-s1, -t1, s1, -t1); 
		pParams1=Vec4(s1, t1, -s1, t1);    
	}

  static CCryNameR pParam0Name("texToTexParams0");
  static CCryNameR pParam1Name("texToTexParams1");

  CShaderMan::m_shPostEffects->FXSetPSFloat(pParam0Name, &pParams0, 1);        
  CShaderMan::m_shPostEffects->FXSetPSFloat(pParam1Name, &pParams1, 1); 

	int nFilter = (bResample && bBigDownsample) ? FILTER_LINEAR: FILTER_POINT;
	if( bCoarseDepthInAlpha )
		nFilter = FILTER_POINT;
	if( pSrc->GetFlags() & FT_CUSTOM_FORMAT ) 
		nFilter = FILTER_POINT;  // Bilinear filtering does not work for custom formats; TODO: Use better kernel in that case
  pSrc->Apply(0, CTexture::GetTexState(STexState(nFilter, true)), -1, -1, (bBindMultisampled && gRenDev->m_RP.m_MSAAData.Type)? 4 : -1);  // bind as msaa target (if valid)

  DrawFullScreenQuad(pDst->GetWidth(), pDst->GetHeight());

  ShEndPass();

  // Restore previous viewport
  gcpRendD3D->FX_PopRenderTarget(0);
  gcpRendD3D->RT_SetViewport(iTempX, iTempY, iWidth, iHeight);        

  gcpRendD3D->FX_Flush();
  PROFILE_SHADER_END    

    PROFILE_LABEL_POP( "STRETCHRECT_EMU" );

  gRenDev->m_RP.m_FlagsShader_RT = nSaveFlagsShader_RT;
}


void SD3DPostEffectsUtils::DownscaleSubregion(CTexture *pSrc, CTexture *&pDst, const RECT * srcRect, const RECT * dstRect, bool bMaxDownsample, bool bChannelSwap)
{








































































}

////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

void SD3DPostEffectsUtils::Downsample(CTexture * pSrc, CTexture * pDst, int nSrcW, int nSrcH, int nDstW, int nDstH, EFilterType eFilter, bool bSetTarget)
{
	if (!pSrc)
		return;

	PROFILE_LABEL_PUSH( "DOWNSAMPLE" );

	PROFILE_SHADER_START

	uint64 nSaveFlagsShader_RT = gRenDev->m_RP.m_FlagsShader_RT;
	gRenDev->m_RP.m_FlagsShader_RT &= ~(g_HWSR_MaskBit[HWSR_SAMPLE0]|g_HWSR_MaskBit[HWSR_SAMPLE1]|g_HWSR_MaskBit[HWSR_SAMPLE2]);

	// Get current viewport
	int iTempX, iTempY, iWidth, iHeight;
	gRenDev->GetViewport(&iTempX, &iTempY, &iWidth, &iHeight);

	if (bSetTarget)
		gcpRendD3D->FX_PushRenderTarget(0, pDst, NULL);
	gcpRendD3D->RT_SetViewport(0, 0, nDstW, nDstH);

	// Currently only exact multiples supported
	Vec2 vSamples(float(nSrcW) / nDstW, float(nSrcH) / nDstH);
	const Vec2 vSampleSize(1.f / nSrcW, 1.f / nSrcH);
	const Vec2 vPixelSize(1.f / nDstW, 1.f / nDstH);
	// Adjust UV space if source rect smaller than texture
	const float fClippedRatioX = float(nSrcW) / pSrc->GetWidth();
	const float fClippedRatioY = float(nSrcH) / pSrc->GetHeight();

	// Base kernel size in pixels
	float fBaseKernelSize = 1.f;
	// How many lines of border samples to skip
	float fBorderSamplesToSkip = 0.f;

	switch (eFilter)
	{
	default:
	case FilterType_Box:
		fBaseKernelSize = 1.f;
		fBorderSamplesToSkip = 0.f;
		break;
	case FilterType_Tent:
		fBaseKernelSize = 2.f;
		fBorderSamplesToSkip = 0.f;
		gRenDev->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_SAMPLE0];
		break;
	case FilterType_Gauss:
		// The base kernel for Gaussian filter is 3x3 pixels [-1.5 .. 1.5]
		// Samples on the borders are ignored due to small contribution
		// so the actual kernel size is N*3 - 2 where N is number of samples per pixel
		fBaseKernelSize = 3.f;
		fBorderSamplesToSkip = 1.f;
		gRenDev->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_SAMPLE1];
		break;
	case FilterType_Lanczos:
		fBaseKernelSize = 3.f;
		fBorderSamplesToSkip = 0.f;
		gRenDev->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_SAMPLE2];
		break;
	}

	// Kernel position step
	const Vec2 vSampleStep(1.f / vSamples.x, 1.f / vSamples.y);
	// The actual kernel radius in pixels
	const Vec2 vKernelRadius = 0.5f * Vec2(fBaseKernelSize, fBaseKernelSize) - fBorderSamplesToSkip * vSampleStep;

	// UV offset from pixel center to first (top-left) sample
	const Vec2 vFirstSampleOffset(0.5f * vSampleSize.x - vKernelRadius.x * vPixelSize.x,
																0.5f * vSampleSize.y - vKernelRadius.y * vPixelSize.y);
	// Kernel position of first (top-left) sample
	const Vec2 vFirstSamplePos = -vKernelRadius + 0.5f * vSampleStep;

	static CCryNameTSCRC pTechName("TextureToTextureResampleFilter");
	ShBeginPass(CShaderMan::m_shPostEffects, pTechName, FEF_DONTSETTEXTURES | FEF_DONTSETSTATES);

	gRenDev->FX_SetState(GS_NODEPTHTEST);

	const Vec4 pParams0(vKernelRadius.x, vKernelRadius.y, fClippedRatioX, fClippedRatioY);
	const Vec4 pParams1(vSampleSize.x, vSampleSize.y, vFirstSampleOffset.x, vFirstSampleOffset.y);
	const Vec4 pParams2(vSampleStep.x, vSampleStep.y, vFirstSamplePos.x, vFirstSamplePos.y);

	static CCryNameR pParam0Name("texToTexParams0");
	static CCryNameR pParam1Name("texToTexParams1");
	static CCryNameR pParam2Name("texToTexParams2");
	CShaderMan::m_shPostEffects->FXSetPSFloat(pParam0Name, &pParams0, 1);
	CShaderMan::m_shPostEffects->FXSetPSFloat(pParam1Name, &pParams1, 1);
	CShaderMan::m_shPostEffects->FXSetPSFloat(pParam2Name, &pParams2, 1);

	SetTexture(pSrc, 0, FILTER_NONE);

	DrawFullScreenQuad(nDstW, nDstH);

	ShEndPass();

	// Restore previous viewport
	if (bSetTarget)
		gcpRendD3D->FX_PopRenderTarget(0);
	gcpRendD3D->RT_SetViewport(iTempX, iTempY, iWidth, iHeight);
	gcpRendD3D->FX_Flush();

	PROFILE_SHADER_END

	PROFILE_LABEL_POP( "DOWNSAMPLE" );

	gRenDev->m_RP.m_FlagsShader_RT = nSaveFlagsShader_RT;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

void SD3DPostEffectsUtils::TexBlurIterative(CTexture *pTex, int nIterationsMul, bool bDilate, CTexture *pBlurTmp)
{
  if(!pTex)
    return;

	SDynTexture *tpBlurTemp = 0;

	if( !pBlurTmp )
	{
		tpBlurTemp = new SDynTexture(pTex->GetWidth(), pTex->GetHeight(), pTex->GetDstFormat(), eTT_2D, FT_STATE_CLAMP | FT_USAGE_RENDERTARGET, "TempBlurRT");
		if(tpBlurTemp)
		tpBlurTemp->Update( pTex->GetWidth(), pTex->GetHeight() );

		if( !tpBlurTemp || !tpBlurTemp->m_pTexture)
		{
			SAFE_DELETE(tpBlurTemp);
			return;
		}
	}

  PROFILE_LABEL_PUSH( "TEXBLUR_16TAPS" );

  PROFILE_SHADER_START

	CTexture *pTempRT = pBlurTmp? pBlurTmp : tpBlurTemp->m_pTexture;
	// Set same EDRAM offsets
	pTempRT->SetRenderTargetTile( pTex->GetRenderTargetTile() );

  // Get current viewport
  int iTempX, iTempY, iWidth, iHeight;
  gRenDev->GetViewport(&iTempX, &iTempY, &iWidth, &iHeight);

	// Iterative blur (aka Kawase): 4 taps, 16 taps, 64 taps, 256 taps, etc
	uint64 nFlagsShaderRT = gRenDev->m_RP.m_FlagsShader_RT;
	gRenDev->m_RP.m_FlagsShader_RT &= ~(g_HWSR_MaskBit[HWSR_SAMPLE0]|g_HWSR_MaskBit[HWSR_SAMPLE1]);

	//// Dilate - use 2 passes, horizontal+vertical
	//if( bDilate )
	//{
	//	gRenDev->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_SAMPLE0];
	//	nIterationsMul = 1;
	//}

	for(int i= 1; i <= nIterationsMul; ++i)
	{				
		////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		// 1st iteration (4 taps)

		gcpRendD3D->FX_PushRenderTarget(0, pTempRT, NULL);
		gcpRendD3D->RT_SetViewport(0, 0, pTex->GetWidth(), pTex->GetHeight()); 

		// only regular gaussian blur supporting masking
		static CCryNameTSCRC pTechName("Blur4Taps");

		uint32 nPasses;
		CShaderMan::m_shPostEffects->FXSetTechnique( pTechName );
		CShaderMan::m_shPostEffects->FXBegin(&nPasses, FEF_DONTSETTEXTURES | FEF_DONTSETSTATES);

		gRenDev->FX_SetState(GS_NODEPTHTEST);   

		// setup texture offsets, for texture sampling
		// Get sample size ratio (based on empirical "best look" approach)
		float fSampleSize = 1.0f * ((float) i);//((float)pTex->GetWidth()/(float)pTex->GetWidth()) * 0.5f;

		// Set samples position
		float s1 = fSampleSize / (float) pTex->GetWidth();  // 2.0 better results on lower res images resizing        
		float t1 = fSampleSize / (float) pTex->GetHeight();       

		// Use rotated grid
		Vec4 pParams0=Vec4(s1*0.96f, t1*0.25f, -s1*0.25f, t1*0.96f);//Vec4(-s1, -t1, s1, -t1); 
		Vec4 pParams1=Vec4(-s1*0.96f, -t1*0.25f, s1*0.25f, -t1*0.96f);//Vec4(s1, t1, -s1, t1);    

		static CCryNameR pParam0Name("texToTexParams0");
		static CCryNameR pParam1Name("texToTexParams1");

		CShaderMan::m_shPostEffects->FXSetPSFloat(pParam0Name, &pParams0, 1);        
		CShaderMan::m_shPostEffects->FXSetPSFloat(pParam1Name, &pParams1, 1); 

		CShaderMan::m_shPostEffects->FXBeginPass(0);

		SetTexture(pTex, 0, FILTER_LINEAR);

		DrawFullScreenQuad(pTex->GetWidth(), pTex->GetHeight());

		CShaderMan::m_shPostEffects->FXEndPass();
		CShaderMan::m_shPostEffects->FXEnd(); 

		gcpRendD3D->FX_PopRenderTarget(0);

		////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		// 2nd iteration (4 x 4 taps)
		if( bDilate )
			gRenDev->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_SAMPLE1];

		gcpRendD3D->FX_PushRenderTarget(0, pTex, NULL);
		gcpRendD3D->RT_SetViewport(0, 0, pTex->GetWidth(), pTex->GetHeight()); 

		CShaderMan::m_shPostEffects->FXSetTechnique( pTechName );
		CShaderMan::m_shPostEffects->FXBegin(&nPasses, FEF_DONTSETTEXTURES | FEF_DONTSETSTATES);

		gRenDev->FX_SetState(GS_NODEPTHTEST);   
		// increase kernel size for second iteration
		fSampleSize = 2.0 * ((float) i);
		// Set samples position
		s1 = fSampleSize / (float) pTex->GetWidth();  // 2.0 better results on lower res images resizing        
		t1 = fSampleSize / (float) pTex->GetHeight();       

		// Use rotated grid
		pParams0=Vec4(s1*0.96f, t1*0.25f, -s1*0.25f, t1*0.96f);//Vec4(-s1, -t1, s1, -t1); 
		pParams1=Vec4(-s1*0.96f, -t1*0.25f, s1*0.25f, -t1*0.96f);//Vec4(s1, t1, -s1, t1);    

		CShaderMan::m_shPostEffects->FXSetPSFloat(pParam0Name, &pParams0, 1);        
		CShaderMan::m_shPostEffects->FXSetPSFloat(pParam1Name, &pParams1, 1); 

		CShaderMan::m_shPostEffects->FXBeginPass(0);

		SetTexture(pTempRT, 0, FILTER_LINEAR);

		DrawFullScreenQuad(pTex->GetWidth(), pTex->GetHeight());

		CShaderMan::m_shPostEffects->FXEndPass();
		CShaderMan::m_shPostEffects->FXEnd(); 

		gcpRendD3D->FX_PopRenderTarget(0);
	}

	gRenDev->m_RP.m_FlagsShader_RT = nFlagsShaderRT;

  // Restore previous viewport
  gcpRendD3D->RT_SetViewport(iTempX, iTempY, iWidth, iHeight);

	pTempRT->SetRenderTargetTile( 0 );
	pTempRT->hintRelease();

  gcpRendD3D->FX_Flush();
  PROFILE_SHADER_END      

  PROFILE_LABEL_POP( "TEXBLUR_16TAPS" );

  SAFE_DELETE(tpBlurTemp);
}

////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

void SD3DPostEffectsUtils::TexBlurDirectional(CTexture *pTex, const Vec2 &vDir, int nIterationsMul, CTexture *pBlurTmp)
{
	if(!pTex)
		return;

	SDynTexture *tpBlurTemp = 0;
	if( !pBlurTmp )
	{
		tpBlurTemp = new SDynTexture(pTex->GetWidth(), pTex->GetHeight(), pTex->GetDstFormat(), eTT_2D, FT_STATE_CLAMP | FT_USAGE_RENDERTARGET, "TempBlurRT");
		if(tpBlurTemp)
			tpBlurTemp->Update( pTex->GetWidth(), pTex->GetHeight() );

		if( !tpBlurTemp || !tpBlurTemp->m_pTexture)
		{
			SAFE_DELETE(tpBlurTemp);
			return;
		}
	}

	PROFILE_LABEL_PUSH( "TEXBLUR_DIRECTIONAL" );

	PROFILE_SHADER_START

	CTexture *pTempRT = pBlurTmp;
	if( !pBlurTmp )
		pTempRT =tpBlurTemp->m_pTexture;

	// Set same EDRAM offsets
  pTempRT->SetRenderTargetTile( pTex->GetRenderTargetTile() );

	static CCryNameTSCRC pTechName("BlurDirectional");
	static CCryNameR pParam0Name("texToTexParams0");
	static CCryNameR pParam1Name("texToTexParams1");
	static CCryNameR pParam2Name("texToTexParams2");
	static CCryNameR pParam3Name("texToTexParams3");

	// Get current viewport
	int iTempX, iTempY, iWidth, iHeight;
	gRenDev->GetViewport(&iTempX, &iTempY, &iWidth, &iHeight);

	// Iterative directional blur: 1 iter: 8 taps, 64 taps, 2 iter: 512 taps, 4096 taps...
	float fSampleScale = 1.0f;
	for(int i= nIterationsMul; i >= 1 ; --i)
	{
		// 1st iteration (4 taps)

		gcpRendD3D->FX_PushRenderTarget(0, pTempRT, NULL);
		gcpRendD3D->RT_SetViewport(0, 0, pTex->GetWidth(), pTex->GetHeight()); 

		uint32 nPasses;
		CShaderMan::m_shPostEffects->FXSetTechnique( pTechName );
		CShaderMan::m_shPostEffects->FXBegin(&nPasses, FEF_DONTSETTEXTURES | FEF_DONTSETSTATES);

		gRenDev->FX_SetState(GS_NODEPTHTEST);   
	
		float fSampleSize = fSampleScale;

		// Set samples position
		float s1 = fSampleSize / (float) pTex->GetWidth();  // 2.0 better results on lower res images resizing        
		float t1 = fSampleSize / (float) pTex->GetHeight();       
		Vec2 vBlurDir;
		vBlurDir.x = s1 * vDir.x;
		vBlurDir.y = t1 * vDir.y;

		// Use rotated grid
		Vec4 pParams0 = Vec4(-vBlurDir.x*4.0f, -vBlurDir.y*4.0f, -vBlurDir.x * 3.0f, -vBlurDir.y * 3.0f); 
		Vec4 pParams1 = Vec4(-vBlurDir.x *2.0f,-vBlurDir.y * 2.0f, -vBlurDir.x , -vBlurDir.y ); 
		Vec4 pParams2 = Vec4(vBlurDir.x *2.0f,vBlurDir.y * 2.0f, vBlurDir.x , vBlurDir.y ); 
		Vec4 pParams3 = Vec4(vBlurDir.x*4.0f, vBlurDir.y*4.0f, vBlurDir.x * 3.0f, vBlurDir.y * 3.0f); 

		CShaderMan::m_shPostEffects->FXSetPSFloat(pParam0Name, &pParams0, 1);        
		CShaderMan::m_shPostEffects->FXSetPSFloat(pParam1Name, &pParams1, 1); 
		CShaderMan::m_shPostEffects->FXSetPSFloat(pParam2Name, &pParams2, 1); 
		CShaderMan::m_shPostEffects->FXSetPSFloat(pParam3Name, &pParams3, 1); 

		CShaderMan::m_shPostEffects->FXBeginPass(0);

		SetTexture(pTex, 0, FILTER_LINEAR, TADDR_BORDER);
		SetTexture(CTexture::s_ptexScreenNoiseMap, 1, FILTER_POINT, 0);

		DrawFullScreenQuad(pTex->GetWidth(), pTex->GetHeight());

		CShaderMan::m_shPostEffects->FXEndPass();
		CShaderMan::m_shPostEffects->FXEnd(); 

		gcpRendD3D->FX_PopRenderTarget(0);

		////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		// 2nd iteration (4 x 4 taps)

		gcpRendD3D->FX_PushRenderTarget(0, pTex, NULL);
		gcpRendD3D->RT_SetViewport(0, 0, pTex->GetWidth(), pTex->GetHeight()); 

		CShaderMan::m_shPostEffects->FXSetTechnique( pTechName );
		CShaderMan::m_shPostEffects->FXBegin(&nPasses, FEF_DONTSETTEXTURES | FEF_DONTSETSTATES);

		gRenDev->FX_SetState(GS_NODEPTHTEST);   
		
		fSampleScale *= 0.75f;

		fSampleSize = fSampleScale;
		s1 = fSampleSize / (float) pTex->GetWidth();  // 2.0 better results on lower res images resizing        
		t1 = fSampleSize / (float) pTex->GetHeight();       
		vBlurDir.x = vDir.x * s1;
		vBlurDir.y = vDir.y * t1;

		pParams0 = Vec4(-vBlurDir.x*4.0f, -vBlurDir.y*4.0f, -vBlurDir.x * 3.0f, -vBlurDir.y * 3.0f); 
		pParams1 = Vec4(-vBlurDir.x *2.0f,-vBlurDir.y * 2.0f, -vBlurDir.x , -vBlurDir.y ); 
		pParams2 = Vec4(vBlurDir.x , vBlurDir.y, vBlurDir.x *2.0f,vBlurDir.y * 2.0f); 
		pParams3 = Vec4(vBlurDir.x * 3.0f, vBlurDir.y * 3.0f, vBlurDir.x*4.0f, vBlurDir.y*4.0f); 

		CShaderMan::m_shPostEffects->FXSetPSFloat(pParam0Name, &pParams0, 1);        
		CShaderMan::m_shPostEffects->FXSetPSFloat(pParam1Name, &pParams1, 1); 
		CShaderMan::m_shPostEffects->FXSetPSFloat(pParam2Name, &pParams2, 1); 
		CShaderMan::m_shPostEffects->FXSetPSFloat(pParam3Name, &pParams3, 1); 

		CShaderMan::m_shPostEffects->FXBeginPass(0);

		SetTexture(pTempRT, 0, FILTER_LINEAR, TADDR_BORDER);
		SetTexture(CTexture::s_ptexScreenNoiseMap, 1, FILTER_POINT, 0);

		DrawFullScreenQuad(pTex->GetWidth(), pTex->GetHeight());

		CShaderMan::m_shPostEffects->FXEndPass();
		CShaderMan::m_shPostEffects->FXEnd(); 

		gcpRendD3D->FX_PopRenderTarget(0);

		fSampleScale *= 0.5f;
	}

	// Restore previous viewport
	gcpRendD3D->RT_SetViewport(iTempX, iTempY, iWidth, iHeight);

	pTempRT->SetRenderTargetTile( 0 );
	pTempRT->hintRelease();

	gcpRendD3D->FX_Flush();
	PROFILE_SHADER_END      

  PROFILE_LABEL_POP( "TEXBLUR_DIRECTIONAL" );

	SAFE_DELETE(tpBlurTemp);
}

////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

void SD3DPostEffectsUtils::TexBlurGaussian(CTexture *pTex, int nAmount, float fScale, float fDistribution, bool bAlphaOnly, CTexture *pMask, bool bSRGB, CTexture *pBlurTmp)
{
  if(!pTex)
    return;

  uint64 nSaveFlagsShader_RT = gRenDev->m_RP.m_FlagsShader_RT;
  gRenDev->m_RP.m_FlagsShader_RT &= ~(g_HWSR_MaskBit[HWSR_SAMPLE0]|g_HWSR_MaskBit[HWSR_SAMPLE1]|g_HWSR_MaskBit[HWSR_SAMPLE2]);

  PROFILE_LABEL_PUSH( "TEXBLUR_GAUSSIAN" );

	if( bSRGB )
		gRenDev->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_SAMPLE0];

  SDynTexture *tpBlurTemp = 0;
	if( !pBlurTmp )
	{
		tpBlurTemp = new SDynTexture(pTex->GetWidth(), pTex->GetHeight(), pTex->GetDstFormat(), eTT_2D,  FT_STATE_CLAMP | FT_USAGE_RENDERTARGET, "TempBlurRT");
		if(tpBlurTemp)
			tpBlurTemp->Update( pTex->GetWidth(), pTex->GetHeight() );

		if( !tpBlurTemp || !tpBlurTemp->m_pTexture)
		{
			SAFE_DELETE(tpBlurTemp);
			return;
		}
	}

	CTexture *pTempRT = pBlurTmp? pBlurTmp : tpBlurTemp->m_pTexture;

	// Set same EDRAM offsets
	pTempRT->SetRenderTargetTile( pTex->GetRenderTargetTile() );

  PROFILE_SHADER_START

    // Get current viewport
    int iTempX, iTempY, iWidth, iHeight;
  gRenDev->GetViewport(&iTempX, &iTempY, &iWidth, &iHeight);
  gcpRendD3D->RT_SetViewport(0, 0, pTex->GetWidth(), pTex->GetHeight());        

  Vec4 vWhite( 1.0f, 1.0f, 1.0f, 1.0f );

  // TODO: Make test with Martin's idea about the horizontal blur pass with vertical offset.

  // only regular gaussian blur supporting masking
  static CCryNameTSCRC pTechName("GaussBlurBilinear");
  static CCryNameTSCRC pTechNameMasked("MaskedGaussBlurBilinear");
  static CCryNameTSCRC pTechName1("GaussAlphaBlur");

  //ShBeginPass(CShaderMan::m_shPostEffects, , FEF_DONTSETTEXTURES | FEF_DONTSETSTATES);
  uint32 nPasses;
  CShaderMan::m_shPostEffects->FXSetTechnique((!bAlphaOnly)? ((pMask)?pTechNameMasked : pTechName) :pTechName1);
  CShaderMan::m_shPostEffects->FXBegin(&nPasses, FEF_DONTSETTEXTURES | FEF_DONTSETSTATES);

  gRenDev->FX_SetState(GS_NODEPTHTEST);   

  // setup texture offsets, for texture sampling
  float s1 = 1.0f/(float) pTex->GetWidth();     
  float t1 = 1.0f/(float) pTex->GetHeight();    

  // Horizontal/Vertical pass params
  const int nSamples = 16;
  int nHalfSamples = (nSamples>>1);

  Vec4 pHParams[32], pVParams[32], pWeightsPS[32];
  float pWeights[32], fWeightSum = 0;

  memset( pWeights,0,sizeof(pWeights) );

  int s;
  for(s = 0; s<nSamples; ++s)
  {
    if(fDistribution != 0.0f)
      pWeights[s] = GaussianDistribution1D(s - nHalfSamples, fDistribution);      
    else
      pWeights[s] = 0.0f;
    fWeightSum += pWeights[s];
  }

  // normalize weights
  for(s = 0; s < nSamples; ++s)
  {
    pWeights[s] /= fWeightSum;  
  }

  // set bilinear offsets
  for(s = 0; s < nHalfSamples; ++s)
  {
    float off_a = pWeights[s*2];
    float off_b = ( (s*2+1) <= nSamples-1 )? pWeights[s*2+1] : 0;   
    float a_plus_b = (off_a + off_b);
    if (a_plus_b == 0)
      a_plus_b = 1.0f;
    float offset = off_b / a_plus_b;

    pWeights[s] = off_a + off_b;
    pWeights[s] *= fScale ;
    pWeightsPS[s] = vWhite * pWeights[s];

    float fCurrOffset = (float) s*2 + offset - nHalfSamples;
    pHParams[s] = Vec4(s1 * fCurrOffset , 0, 0, 0);  
    pVParams[s] = Vec4(0, t1 * fCurrOffset , 0, 0);       
  }


  STexState sTexState = STexState(FILTER_LINEAR, true);
  static CCryNameR pParam0Name("psWeights");
  static CCryNameR pParam1Name("PI_psOffsets");

  //SetTexture(pTex, 0, FILTER_LINEAR); 
  CShaderMan::m_shPostEffects->FXSetPSFloat(pParam0Name, pWeightsPS, nHalfSamples);  

  //for(int p(1); p<= nAmount; ++p)   
  {
    //Horizontal


    gcpRendD3D->FX_PushRenderTarget(0, pTempRT, NULL);
    gcpRendD3D->RT_SetViewport(0, 0, pTex->GetWidth(), pTex->GetHeight());        

    // !force updating constants per-pass! (dx10..)
    CShaderMan::m_shPostEffects->FXBeginPass(0);

    pTex->Apply(0, CTexture::GetTexState(sTexState)); 
    if( pMask )
      pMask->Apply(1, CTexture::GetTexState(sTexState));
    CShaderMan::m_shPostEffects->FXSetVSFloat(pParam1Name, pHParams, nHalfSamples);                
    DrawFullScreenQuad(pTex->GetWidth(), pTex->GetHeight());

    CShaderMan::m_shPostEffects->FXEndPass();

    gcpRendD3D->FX_PopRenderTarget(0);

    //Vertical
    gcpRendD3D->FX_PushRenderTarget(0, pTex, NULL);
    gcpRendD3D->RT_SetViewport(0, 0, pTex->GetWidth(), pTex->GetHeight());         

    // !force updating constants per-pass! (dx10..)
    CShaderMan::m_shPostEffects->FXBeginPass(0);

    CShaderMan::m_shPostEffects->FXSetVSFloat(pParam1Name, pVParams, nHalfSamples);
    pTempRT->Apply(0, CTexture::GetTexState(sTexState)); 
    if( pMask )
      pMask->Apply(1, CTexture::GetTexState(sTexState));
    DrawFullScreenQuad(pTex->GetWidth(), pTex->GetHeight());      

    CShaderMan::m_shPostEffects->FXEndPass();

    gcpRendD3D->FX_PopRenderTarget(0);
  }             

  CShaderMan::m_shPostEffects->FXEnd(); 

  //    ShEndPass( );

  // Restore previous viewport
  gcpRendD3D->RT_SetViewport(iTempX, iTempY, iWidth, iHeight);

	// Set same EDRAM offsets
	pTempRT->SetRenderTargetTile( 0 );
	pTempRT->hintRelease();

  //release dyntexture
  SAFE_DELETE(tpBlurTemp);

  gcpRendD3D->FX_Flush();
  PROFILE_SHADER_END      

    PROFILE_LABEL_POP( "TEXBLUR_GAUSSIAN" );

  gRenDev->m_RP.m_FlagsShader_RT = nSaveFlagsShader_RT;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

void SD3DPostEffectsUtils::BilateralBlurring(CTexture *pSrc, CTexture *pDest)
{
	PROFILE_LABEL_PUSH( "BILATERAL_BLURRING" );

	uint64 nSaveFlagsShader_RT = gRenDev->m_RP.m_FlagsShader_RT;

	// Clear the sample flags
	gRenDev->m_RP.m_FlagsShader_RT &= ~(g_HWSR_MaskBit[HWSR_SAMPLE0]|g_HWSR_MaskBit[HWSR_SAMPLE1]|g_HWSR_MaskBit[HWSR_SAMPLE2]);

	PROFILE_SHADER_START

	int destWidth = pDest->GetWidth(), 
			destHeight = pDest->GetHeight();

	// Get current viewport
	int iTempX, iTempY, iWidth, iHeight;
	gRenDev->GetViewport(&iTempX, &iTempY, &iWidth, &iHeight);

	gcpRendD3D->FX_PushRenderTarget(0, pDest, NULL);
	gcpRendD3D->RT_SetViewport(0, 0, destWidth, destHeight); 

	static CCryNameTSCRC techName("BilateralBlurring");
	uint32 nPasses;

	CShaderMan::m_shPostEffects->FXSetTechnique( techName );
	CShaderMan::m_shPostEffects->FXBegin(&nPasses, FEF_DONTSETTEXTURES | FEF_DONTSETSTATES);

	gRenDev->FX_SetState(gRenDev->m_RP.m_CurState | GS_NODEPTHTEST);   

	// Setup shader constants
	int srcWidth = pSrc->GetWidth();
	int srcHeight = pSrc->GetHeight();

	bool resample = (destWidth > srcWidth || destHeight > srcHeight);

	static CCryNameR paramBlurOffset("BlurOffset");
	static CCryNameR paramBlurKernel("BlurKernel");
	Vec4 v;

#if HALF_PIXEL_SHIFT_NEEDED
	v[0] = 0.5f / destWidth; v[1] = 0.5f / destHeight;
#else
	v[0] = 0; v[1] = 0;
#endif
	v[2] = srcWidth; v[3] = srcHeight;
	CShaderMan::m_shPostEffects->FXSetVSFloat(paramBlurOffset, &v, 1);

	// X Blur
	v[0] = 1.0f / srcWidth; v[1] = 1.0f / srcHeight; v[2] = srcWidth; v[3] = srcHeight;
	CShaderMan::m_shPostEffects->FXSetPSFloat(paramBlurOffset, &v, 1);

	v[0] = 2.f / srcWidth; v[1] = 0; v[2] = 2.f / srcHeight; v[3] = 0;
	CShaderMan::m_shPostEffects->FXSetPSFloat(paramBlurKernel, &v, 1);

	if (resample)
	{
		// Size mismatch, the surface is probably upscaled
		if (CTexture::s_ptexZTargetScaled)
			gRenDev->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_SAMPLE0];
	}

	CShaderMan::m_shPostEffects->FXBeginPass(0);

	SetTexture(pSrc, 0, FILTER_LINEAR, 1, (pSrc->GetFlags() & FT_USAGE_ALLOWREADSRGB) != 0);
	SetTexture(CTexture::s_ptexZTarget, 1, FILTER_POINT);

	if (resample && CTexture::s_ptexZTargetScaled)
		SetTexture(CTexture::s_ptexZTargetScaled, 2, FILTER_POINT);

	DrawFullScreenQuad(destWidth, destHeight);

	CShaderMan::m_shPostEffects->FXEndPass();
	CShaderMan::m_shPostEffects->FXEnd(); 

	gcpRendD3D->FX_PopRenderTarget(0);	

	// Restore previous viewport
	gcpRendD3D->RT_SetViewport(iTempX, iTempY, iWidth, iHeight);

	gcpRendD3D->FX_Flush();
	PROFILE_SHADER_END      

	gRenDev->m_RP.m_FlagsShader_RT = nSaveFlagsShader_RT;

	PROFILE_LABEL_POP( "BILATERAL_BLURRING" );
}

////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////












































////////////////////////////////////////////////////////////////////////////////////////////////////

void SD3DPostEffectsUtils::BeginStencilPrePass(const bool bAddToStencil, const bool bDebug, const bool bResetStencil, const uint8 nStencilRefReset)
{
  if( !bAddToStencil && !bResetStencil)
    gcpRendD3D->m_nStencilMaskRef++;		

  if (gcpRendD3D->m_nStencilMaskRef>STENC_MAX_REF)
  {
    gcpRendD3D->EF_ClearBuffers(FRT_CLEAR_STENCIL|FRT_CLEAR_IMMEDIATE, NULL);
    gcpRendD3D->m_nStencilMaskRef= 1;
  }

  gcpRendD3D->FX_SetStencilState(
    STENC_FUNC(FSS_STENCFUNC_ALWAYS) |
    STENCOP_FAIL(FSS_STENCOP_REPLACE) |
    STENCOP_ZFAIL(FSS_STENCOP_REPLACE) |
    STENCOP_PASS(FSS_STENCOP_REPLACE),
    bResetStencil? nStencilRefReset: gcpRendD3D->m_nStencilMaskRef, 0xFFFFFFFF, 0xFFFF
    );








  gcpRendD3D->FX_SetState(GS_STENCIL|GS_NODEPTHTEST|(!bDebug?GS_COLMASK_NONE:0));  
}

////////////////////////////////////////////////////////////////////////////////////////////////////

void SD3DPostEffectsUtils::EndStencilPrePass()
{






}

////////////////////////////////////////////////////////////////////////////////////////////////////

void SD3DPostEffectsUtils::SetupStencilStates( int32 nStFunc )
{
  if( nStFunc < 0 )
  {



    return;
  }

  gcpRendD3D->FX_SetStencilState(
    STENC_FUNC(nStFunc) |
    STENCOP_FAIL(FSS_STENCOP_KEEP) |
    STENCOP_ZFAIL(FSS_STENCOP_KEEP) |
    STENCOP_PASS(FSS_STENCOP_KEEP),
    gcpRendD3D->m_nStencilMaskRef, 0xFFFFFFFF, 0xFFFFFFFF);







}

////////////////////////////////////////////////////////////////////////////////////////////////////

void SD3DPostEffectsUtils::BackBufferTexturePush(bool bPushPrev, bool bForceResolve)
{






	CTexture *pBackBufferTexs[2] =
	{		
		CTexture::s_ptexBackBuffer,
		CTexture::s_ptexPrevBackBuffer[0],
	};

	m_pCurrBackBuffer[1] = m_pCurrBackBuffer[0];
	m_pCurrBackBuffer[0] = pBackBufferTexs[bPushPrev? ((m_nCurrBackBufferID+1) % 2) : m_nCurrBackBufferID];

	gcpRendD3D->FX_PushRenderTarget(0, m_pCurrBackBuffer[0], &gcpRendD3D->m_DepthBufferOrig);
	gcpRendD3D->RT_SetViewport(0, 0, m_pCurrBackBuffer[0]->GetWidth(), m_pCurrBackBuffer[0]->GetHeight());         

	if( !bPushPrev )
		m_nCurrBackBufferID = (m_nCurrBackBufferID+1) % 2;

}

////////////////////////////////////////////////////////////////////////////////////////////////////

void SD3DPostEffectsUtils::BackBufferTexturePop()
{
#if !defined(XENON)

	if( m_pCurrBackBuffer[0] && m_pCurrBackBuffer[0] != m_pCurrBackBuffer[1])
		gcpRendD3D->FX_PopRenderTarget(0);				

#endif
}

CTexture *SD3DPostEffectsUtils::GetBackBufferTexture()
{
	return (!m_pCurrBackBuffer[1])? CTexture::s_ptexBackBuffer : m_pCurrBackBuffer[1];
}

void SD3DPostEffectsUtils::SetFillModeSolid(bool bEnable)
{
	if( bEnable )
	{
		if (gcpRendD3D->GetWireframeMode() > R_SOLID_MODE)
		{
#if defined (DIRECT3D9)
			gcpRendD3D->m_pd3dDevice->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID);
#elif defined (DIRECT3D10)
			SStateRaster RS = gcpRendD3D->m_StatesRS[gcpRendD3D->m_nCurStateRS];
			RS.Desc.FillMode = D3D11_FILL_SOLID;
			gcpRendD3D->SetRasterState(&RS);
#endif
		}
	}
	else
	{
		if(gcpRendD3D->GetWireframeMode() == R_WIREFRAME_MODE)
		{
#if defined (DIRECT3D9)
			gcpRendD3D->m_pd3dDevice->SetRenderState(D3DRS_FILLMODE, D3DFILL_WIREFRAME);
#elif defined (DIRECT3D10)
			SStateRaster RS = gcpRendD3D->m_StatesRS[gcpRendD3D->m_nCurStateRS];
			RS.Desc.FillMode = D3D11_FILL_WIREFRAME;
			gcpRendD3D->SetRasterState(&RS);
#endif
		}
		else
			if(gcpRendD3D->GetWireframeMode() == R_POINT_MODE)
			{
#if defined (DIRECT3D9)
				gcpRendD3D->m_pd3dDevice->SetRenderState(D3DRS_FILLMODE, D3DFILL_POINT);




#endif
			}
	}
}

/////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////

void CGammaReference::Render()
{  
  PROFILE_SHADER_START

    uint64 nSaveFlagsShader_RT = gRenDev->m_RP.m_FlagsShader_RT;
  gcpRendD3D->m_RP.m_FlagsShader_RT &= ~(g_HWSR_MaskBit[HWSR_SAMPLE0]|g_HWSR_MaskBit[HWSR_SAMPLE1]|g_HWSR_MaskBit[HWSR_SAMPLE2]);






  // Show just reference image 
  if( CRenderer::CV_r_showgammareference == 2 )   
  {
    gcpRendD3D->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_SAMPLE1];
    if( !m_pGammaReference )
    {
      m_pGammaReference = CTexture::ForName("Textures/Defaults/calibration.tif",  FT_DONT_RELEASE | FT_DONT_RESIZE | FT_DONT_STREAM, eTF_Unknown);      
      m_pGammaReference->SRGBRead( false );
    }
  }
  
  static CCryNameTSCRC TechName("GammaReference");
  PostProcessUtils().ShBeginPass(CShaderMan::m_shPostEffects, TechName, FEF_DONTSETTEXTURES | FEF_DONTSETSTATES);                    

  uint32 nRenderState = GS_NODEPTHTEST;
  if( CRenderer::CV_r_showgammareference != 2 ) 
    nRenderState |= GS_BLSRC_SRCALPHA | GS_BLDST_ONEMINUSSRCALPHA;
  else
    PostProcessUtils().SetTexture(m_pGammaReference, 0, FILTER_LINEAR, 1);

  gcpRendD3D->FX_SetState( nRenderState );

  PostProcessUtils().DrawFullScreenQuad(CTexture::s_ptexBackBuffer->GetWidth(), CTexture::s_ptexBackBuffer->GetHeight());

  PostProcessUtils().ShEndPass();                    

  if( CRenderer::CV_r_showgammareference != 2 ) 
  {
    gcpRendD3D->Set2DMode(false, 1, 1);           
    SDrawTextInfo pDrawTexInfo;    

    pDrawTexInfo.color[0] = pDrawTexInfo.color[2] = 0.0f;
    pDrawTexInfo.color[1] = 1.0f;

    gcpRendD3D->Draw2dText(50, 10, " " , pDrawTexInfo); // hack to avoid garbage - something broken with draw2Dtext
    gcpRendD3D->Draw2dText(50, 400, "Linear:", pDrawTexInfo);

    gcpRendD3D->Draw2dText(50, 520, "SRGB (2.2):", pDrawTexInfo);

    gcpRendD3D->Draw2dText(10, 10, "Please adjust your display brightness/contrast", pDrawTexInfo);  

    pDrawTexInfo.color[0] = 1;
    pDrawTexInfo.color[1] = pDrawTexInfo.color[2] = 0.0f;

    gcpRendD3D->Draw2dText(240, 100, "Middle square color should blend with background color", pDrawTexInfo);   
    gcpRendD3D->Draw2dText(260, 340, "Each individual shade should be clearly seen", pDrawTexInfo);

    gcpRendD3D->Set2DMode(true, 1, 1);           
  }

  gcpRendD3D->FX_Flush();
  PROFILE_SHADER_END

    gRenDev->m_RP.m_FlagsShader_RT = nSaveFlagsShader_RT;
}

/////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////

bool CD3D9Renderer::FX_PostProcessScene(bool bEnable)
{  
  if( !gcpRendD3D )
  {
    return false;
  }

#if !defined(XENON) && !defined(PS3)
	// can skip checking resolution update for consoles - always fixed resolution

  if(bEnable)
  {    
    //if(!CTexture::s_ptexBackBuffer)
    {      
      // Create all resources if required
      PostProcessUtils().Create();
    }
  }
  else
  if (!CRenderer::CV_r_PostProcess &&  !gRenDev->IsHDRModeEnabled() && CTexture::s_ptexBackBuffer)
  {
    PostProcessUtils().Release();
  }

#endif

  return true;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
// Temporary hack for hdr post effects

void CD3D9Renderer::FX_PostProcessSceneHDR()
{
	if ( !gcpRendD3D ||  !gRenDev->IsHDRModeEnabled() || !CTexture::s_ptexSceneTarget || !CRenderer::CV_r_PostProcess)
		return;

	if( !CRenderer::CV_r_MotionBlur && !CRenderer::CV_r_dof && !CRenderer::CV_r_snow )
		return;
	//bool bHiSpecs = CPostEffectsMgr::CheckPostProcessQuality( eRQ_Low, eSQ_High );
	//if( !bQualityCheck )
	//	return;

	if( PostEffectMgr()->GetEffects().empty() || gcpRendD3D->GetWireframeMode() > R_SOLID_MODE) 
		return;

	if( !CShaderMan::m_shPostEffects )
		return;

	if( !CTexture::IsTextureExist(CTexture::s_ptexBackBuffer) )
		return;

	if( !CTexture::IsTextureExist(CTexture::s_ptexSceneTarget) ) 
		return;

	gcpRendD3D->Set2DMode(true, 1, 1);     

	PostProcessUtils().m_pCurDepthSurface = &gcpRendD3D->m_DepthBufferOrig;

	CPostEffectsMgr *pPostMgr = PostEffectMgr();

	// Apply motion blur / dof
	CMotionBlur *pMotionBlur = (CMotionBlur *) pPostMgr->GetEffect(ePFX_eMotionBlur);
	pMotionBlur->Render();

	// Render snow particles.
	CSceneSnow *pSceneSnow = (CSceneSnow *) pPostMgr->GetEffect(ePFX_SceneSnow);
	if(pSceneSnow->IsActiveSnow())
		pSceneSnow->Render();

	gcpRendD3D->Set2DMode(false, 1, 1);     
}

////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

void CD3D9Renderer::GetReprojectionMatrix(Matrix44A & matReproj,
																					const Matrix44A & matView,
																					const Matrix44A & matProj,
																					const Matrix44A & matPrevView,
																					const Matrix44A & matPrevProj,
																					float fFarPlane) const
{
	// Current camera screen-space to projection-space
	const Matrix44A matSc2Pc
		( 2.f,  0.f, -1.f,  0.f,
		  0.f,  2.f, -1.f,  0.f,
		  0.f,  0.f,  1.f,  0.f,
		  0.f,  0.f,  0.f,  1.f / fFarPlane );

	// Current camera view-space to projection-space
	const Matrix44A matVc2Pc
		( matProj.m00,  0.f,          0.f,         0.f,
		  0.f,          matProj.m11,  0.f,         0.f,
		  0.f,          0.f,          1.f,         0.f,
		  0.f,          0.f,          0.f,         1.f );

	// Current camera projection-space to world-space
	const Matrix44A matPc2Wc = (matVc2Pc * matView).GetInverted();

	// Previous camera view-space to projection-space
	const Matrix44A matVp2Pp
		( matPrevProj.m00, 0.f,              0.f,                0.f,
		  0.f,             matPrevProj.m11,  0.f,                0.f,
		  0.f,             0.f,              1.f,                0.f,
		  0.f,             0.f,              0.f,                1.f );

	// Previous camera world-space to projection-space
	const Matrix44A matWp2Pp = matVp2Pp * matPrevView;

	// Previous camera projection-space to texture-space
	const Matrix44A matPp2Tp
		( 0.5f,         0.f,           0.5f,                     0.f,
		  0.f,          0.5f,          0.5f,                     0.f,
		  0.f,          0.f,           1.f,                      0.f,
		  0.f,          0.f,           0.f,                      1.f );

	// Final reprojection matrix (from current camera screen-space to previous camera texture-space)
	matReproj = matPp2Tp * matWp2Pp * matPc2Wc * matSc2Pc;
}

/////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////

void CPostEffectsMgr::Begin()
{   
  PostProcessUtils().Log("### POST-PROCESSING BEGINS ### "); 
  PostProcessUtils().m_pTimer=gEnv->pTimer;    
	
	if( !gRenDev->IsHDRModeEnabled() )
		SPostEffectsUtils::m_iFrameCounter=(SPostEffectsUtils::m_iFrameCounter+1)%1000;  

  static EShaderQuality nPrevShaderQuality = eSQ_Low;
  static ERenderQuality nPrevRenderQuality = eRQ_Low;

  EShaderQuality nShaderQuality = (EShaderQuality) gcpRendD3D->EF_GetShaderQuality(eST_PostProcess);
  ERenderQuality nRenderQuality = gRenDev->m_RP.m_eQuality;
  if( nPrevShaderQuality != nShaderQuality || nPrevRenderQuality != nRenderQuality )
  {
    CPostEffectsMgr::Reset(true);
    nPrevShaderQuality = nShaderQuality;
    nPrevRenderQuality = nRenderQuality;
  }

	 
	// Avoid using ResetToDefault - it's reseting everything, including gpr's states - we leave this comment out for now
	// in case any problems
	//gcpRendD3D->ResetToDefault();

  gcpRendD3D->FX_ResetPipe();  

  gcpRendD3D->Set2DMode(true, 1, 1);       
	SPostEffectsUtils::m_pCurDepthSurface = &gcpRendD3D->m_DepthBufferOrig;
	
  gcpRendD3D->RT_SetViewport(0, 0, gcpRendD3D->GetWidth(), gcpRendD3D->GetHeight());

  SPostEffectsUtils::m_fWaterLevel = gRenDev->m_p3DEngineCommon.m_OceanInfo.m_fWaterLevel;

	PostProcessUtils().SetFillModeSolid(true);
}

void CPostEffectsMgr::End()
{













  gcpRendD3D->Set2DMode(false, 1, 1);

  gcpRendD3D->RT_SetViewport(PostProcessUtils().m_pScreenRect.left, PostProcessUtils().m_pScreenRect.top, PostProcessUtils().m_pScreenRect.right, PostProcessUtils().m_pScreenRect.bottom); 

	// Avoid using ResetToDefault - it's reseting everything, including gpr's states - we leave this comment out for now
	// in case any problems
	//gcpRendD3D->ResetToDefault();

  gcpRendD3D->FX_ResetPipe();  

  gcpRendD3D->m_RP.m_TI[gcpRendD3D->m_RP.m_nProcessThreadID].m_PersFlags &= ~RBPF_POSTPROCESS;

	PostProcessUtils().SetFillModeSolid(false);

  PostEffectMgr()->ResetLights();

	PostProcessUtils().ResetBackBufferTexStates();

	//CTexture::s_ptexBackBufferScaled[0]->hintRelease();
	//CTexture::s_ptexBackBufferScaled[1]->hintRelease();
	//CTexture::s_ptexBackBufferScaled[2]->hintRelease();

	// cannot release, updated only every couple frames
	//CTexture::s_ptexWaterVolumeDDN->hintRelease();

  gcpRendD3D->m_RP.m_PersFlags2 &= ~RBPF2_NOPOSTAA;

	const uint32 nThreadID = gRenDev->m_RP.m_nProcessThreadID;
	int32 nR = SRendItem::m_RecurseLevel[nThreadID]-1;
# ifndef _RELEASE
  if (nR<0) __debugbreak();
# endif
	int32 nRenderView = SRendItem::m_RenderView[nThreadID][nR];
#ifdef SEG_WORLD
	gRenDev->m_CameraMatrixPrev/*[nThreadID][ min(1, nRenderView) ]*/= gcpRendD3D->GetCameraMatrix();
#else
	gRenDev->m_CameraMatrixPrev/*[nThreadID][ min(1, nRenderView) ]*/= gcpRendD3D->m_CameraMatrix;
#endif
	gRenDev->m_CameraMatrixNearestPrev=gRenDev->m_CameraMatrixNearest;

  PostProcessUtils().Log("### POST-PROCESSING ENDS ### ");   
}

 
/////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////

bool CREPostProcess:: mfDraw(CShader *ef, SShaderPass *sfm)
{ 
	CPostEffectsMgr *pPostMgr = PostEffectMgr();
  IF( !gcpRendD3D || !CRenderer::CV_r_PostProcess || pPostMgr->GetEffects().empty() || gcpRendD3D->GetWireframeMode() > R_SOLID_MODE, 0) 
    return 0;

	// Skip hdr/post processing when rendering different camera views
	if( gcpRendD3D->m_RP.m_TI[gcpRendD3D->m_RP.m_nProcessThreadID].m_PersFlags&RBPF_MIRRORCULL )
		return 0;

  if (gcpRendD3D->m_bDeviceLost)
    return 0;

  IF( !CShaderMan::m_shPostEffects, 0)
    return 0;

  IF( !CTexture::IsTextureExist(CTexture::s_ptexBackBuffer), 0)
    return 0;
     
  IF( !CTexture::IsTextureExist(CTexture::s_ptexSceneTarget), 0) 
    return 0;

	GPU_TIMER_START( "POSTFX" );
	PROFILE_LABEL_PUSH("POST EFFECTS");





  pPostMgr->Begin();   

  gcpRendD3D->FX_ApplyShaderQuality(eST_PostProcess);

  for(CPostEffectItor pItor = pPostMgr->GetEffects().begin(), pItorEnd = pPostMgr->GetEffects().end(); pItor != pItorEnd; ++pItor)
  {
    CPostEffect *pCurrEffect = (*pItor);
    if( pCurrEffect->GetRenderFlags() & PSP_REQUIRES_UPDATE )
      pCurrEffect->Update();
  }

	CPostEffectVec& activeEffects = pPostMgr->GetActiveEffects(gcpRendD3D->m_RP.m_nProcessThreadID);
	activeEffects.clear();

  for(CPostEffectItor pItor = pPostMgr->GetEffects().begin(), pItorEnd = pPostMgr->GetEffects().end(); pItor != pItorEnd; ++pItor)
  {
    CPostEffect *pCurrEffect = (*pItor);
    if(pCurrEffect->Preprocess())  
    {
			uint32 nRenderFlags = pCurrEffect->GetRenderFlags();
			if( nRenderFlags & PSP_UPDATE_BACKBUFFER )
			{
				//PostProcessUtils().BackBufferTexturePush();
				PostProcessUtils().CopyScreenToTexture(CTexture::s_ptexBackBuffer);
			}

			activeEffects.push_back( pCurrEffect );
			pCurrEffect->Render();

			//if( nRenderFlags & PSP_UPDATE_BACKBUFFER )
			//	PostProcessUtils().BackBufferTexturePop();
    }
  }

	//CTexture *pTex = PostProcessUtils().GetBackBufferTexture();			
	//PostProcessUtils().CopyTextureToScreen( pTex );
	//pTex->SaveJPG("test.jpg");

  // Output active post effects
  if( CRenderer::CV_r_PostProcess >= 2 && !activeEffects.empty() )
  {               
    CPostEffectItor pNameItor;        
    CPostEffectItor pNameEndItor = activeEffects.end();        

    // current output line position
    int nPosY = 20;  
    gcpRendD3D->Set2DMode(false, 1, 1);           
    SDrawTextInfo pDrawTexInfo;    
    
    pDrawTexInfo.color[0] = pDrawTexInfo.color[2] = 0.0f;
    pDrawTexInfo.color[1] = 1.0f;

    gcpRendD3D->Draw2dText(30, nPosY, " " , pDrawTexInfo); // hack to avoid garbage - someting broken with draw2Dtext
    gcpRendD3D->Draw2dText(30, nPosY, "Active post effects:", pDrawTexInfo);
    nPosY += 15;

    pDrawTexInfo.color[0] = pDrawTexInfo.color[1] = pDrawTexInfo.color[2] = 1.0f;

    for(pNameItor = activeEffects.begin(); pNameItor != pNameEndItor; ++pNameItor) 
    {
			CPostEffect* pEffect = *pNameItor;

      // Output active post effects
      gcpRendD3D->Draw2dText(30, nPosY, pEffect->GetName(), pDrawTexInfo);
      nPosY += 10;
    }

		if( CRenderer::CV_r_SSAO )
		{
			gcpRendD3D->Draw2dText(30, nPosY, "SSAO", pDrawTexInfo);
		}

    gcpRendD3D->Set2DMode(true, 1, 1);           
  }

  if( CRenderer::CV_r_PostProcess == 3  )
  {
    StringEffectMap *pEffectsParamsUpdated = pPostMgr->GetDebugParamsUsedInFrame();
    if( pEffectsParamsUpdated && !pEffectsParamsUpdated->empty())
    {
      StringEffectMapItor pItor = pEffectsParamsUpdated->begin();
      StringEffectMapItor pEnd = pEffectsParamsUpdated->end();

      // current output line position
      int nPosX = 250, nPosY = 5;  
      gcpRendD3D->Set2DMode(false, 1, 1);
      SDrawTextInfo pDrawTexInfo;    
      pDrawTexInfo.color[0] = 0.0f;
      pDrawTexInfo.color[2] = 0.0f;
      pDrawTexInfo.color[1] = 1.0f;
      gcpRendD3D->Draw2dText(nPosX, nPosY, " " , pDrawTexInfo); // hack to avoid garbage - something broken with draw2Dtext
      gcpRendD3D->Draw2dText(nPosX, nPosY, "Frame parameters:", pDrawTexInfo);
      nPosY += 15;

      pDrawTexInfo.color[0] = 1.0f;
      pDrawTexInfo.color[1] = 1.0f;
      pDrawTexInfo.color[2] = 1.0f;
      for( ; pItor != pEnd; ++pItor )
      {
        const char *pName = (pItor->first).c_str();
        char pNameAndValue[128];
        sprintf(pNameAndValue, "%s: %.4f\n", (pItor->first).c_str(), (pItor->second!=0)?pItor->second->GetParam():0.0f );
        gcpRendD3D->Draw2dText(nPosX, nPosY, pNameAndValue, pDrawTexInfo);   
        //CryLogAlways(pNameAndValue);

        nPosY += 10;
      }

      gcpRendD3D->Set2DMode(true, 1, 1);           
      pEffectsParamsUpdated->clear();
    }
  }
  
  pPostMgr->End(); 




  
  PROFILE_LABEL_POP( "POST EFFECTS" );
	GPU_TIMER_STOP( "POSTFX" );

  return 1;
}
 
